package markdown

import (
	"bytes"
	"encoding/json"
	_ "encoding/json"
	"fmt"
	"github.com/thedevsaddam/gojsonq/v2"
	"io"
	"net/http"
	"showdocApi/logs"
	"strings"
	"text/template"
)

type resolver struct {
	jq             *gojsonq.JSONQ
	showDocDetails showDocParam
	definitions    map[string]interface{}
	components     *Components
}

func Init(body []byte, apiKey, apiToken, showDocHost string) (*resolver, error) {
	r := new(resolver)
	r.showDocDetails.Url = fmt.Sprintf(url, showDocHost)
	r.showDocDetails.ApiToken = apiToken
	r.showDocDetails.ApiKey = apiKey

	result := make(map[string]interface{})
	if err := json.Unmarshal(body, &result); err != nil {
		return nil, err
	}
	marshal, _ := json.Marshal(result)
	processor := gojsonq.New().FromString(string(marshal))
	if err := processor.Error(); err != nil {
		return nil, err
	}
	r.jq = processor
	// 获取定义
	r.getDefinitions()
	r.getComponents()
	return r, nil
}

func (r *resolver) Homepage() error {
	tags := r.parseTags()
	details := r.parseDetails()
	for _, tag := range tags {
		for path, value := range r.getPaths() {
			//按method分页。
			for method, value := range value.(map[string]interface{}) {
				operationInfo := new(OperationInfo)
				deserialization(value, operationInfo)
				var exist = false
				for _, t := range operationInfo.Tags {
					if tag.Name == t {
						exist = true
					}
				}
				if !exist {
					continue
				}
				tag.ApiList += fmt.Sprintf("- `%s %s`：%s \n", method, path, operationInfo.Summary)
			}
		}
		overview, err := parseTemplate(templateOverview, tag)
		if err != nil {
			return err
		}
		details.Overview += overview
	}
	homepage, err := parseTemplate(templateHomepage, details)
	if err != nil {
		return err
	}
	//页面内容格式化
	r.showDocDetails.PageContent = homepage
	//页面标题
	r.showDocDetails.PageTitle = "Seeds Auth REST API总览"
	r.showDocDetails.SNumber = 0
	r.post()
	return err
}

func (r *resolver) Generate() error {
	//主页
	if err := r.Homepage(); err != nil {
		return fmt.Errorf("homepage: %v", err)
	}
	for path, value := range r.getPaths() {
		//请求Url
		apiUrl := fmt.Sprintf("%s%s%s", r.getHost(), r.getBasePath(), path)
		//按method分页。
		for method, value := range value.(map[string]interface{}) {
			logs.Debug("------", path, method)
			var err error
			operationInfo := new(OperationInfo)
			deserialization(value, operationInfo)
			pageInfo := new(PageInfo)
			pageInfo.ApiUrl = apiUrl
			pageInfo.Method = method
			pageInfo.Description = operationInfo.Description
			//是否已废弃
			templateDocPage := templatePage
			if operationInfo.Deprecated {
				templateDocPage = "<div style='background-color: #ffeeba; color: #856404; border-left: 6px solid #ffc107; padding: 10px; margin-bottom: 15px;text-align: center;'><strong>⚠️ **注意**：</strong> 此接口已废弃。</div>\n\n" + templatePage
			}
			//目录标题
			r.showDocDetails.CatName = strings.Join(operationInfo.Tags, "")
			/*请求参数解析封装文本*/
			var reqParamTableList = ""
			var reqParamDescList = ""
			for _, parameter := range operationInfo.Parameters {
				reqParamTable, reqParamDesc := parameter.requestParam(r)
				reqParamTableList += reqParamTable
				reqParamDescList += reqParamDesc
			}
			if operationInfo.RequestBody != nil {
				// openapi 3.0
				reqParamDescList += operationInfo.RequestBody.requestBody(r)
			}
			if reqParamTableList+reqParamDescList != "" {
				pageInfo.RequestParam = reqParamTableList + reqParamDescList
			}
			if reqParamTableList != "" {
				pageInfo.RequestParam = fmt.Sprintf(templateParamReq, pageInfo.RequestParam)
			}
			if pageInfo.RequestParam == "" {
				pageInfo.RequestParam = "\n- 暂无请求参数描述 \n\n"
			}

			/*返回参数解析封装*/
			var resParamTableList = ""
			var resParamDescList = ""
			for code, response := range operationInfo.Responses {
				resParamTable, resParamDesc := response.responseParam(code, r)
				resParamTableList += resParamTable
				resParamDescList += resParamDesc
			}
			if resParamTableList+resParamDescList != "" {
				pageInfo.ResponseParam = resParamTableList + resParamDescList
			}
			if resParamTableList != "" {
				pageInfo.ResponseParam = fmt.Sprintf(templateParamResp, pageInfo.ResponseParam)
			}
			if pageInfo.ResponseParam == "" {
				pageInfo.ResponseParam = "\n- 暂无返回参数描述 \n\n"
			}
			//页面内容格式化
			r.showDocDetails.PageContent, err = parseTemplate(templateDocPage, pageInfo)
			if err != nil {
				logs.Error("page content format failed.err[%s]", err.Error())
				return err
			}
			//页面标题
			r.showDocDetails.PageTitle = operationInfo.Summary
			r.showDocDetails.SNumber = 1
			r.post()
		}
	}
	return nil
}

func parseTemplate(templateName string, body interface{}) (string, error) {
	tmpl, err := template.New("markdown").Parse(templateName)
	if err != nil {
		return "", err
	}
	var buf bytes.Buffer
	if err = tmpl.Execute(&buf, body); err != nil {
		return "", err
	}
	return buf.String(), nil
}

func (r *resolver) post() {
	errInfo := fmt.Sprintf("CatName:%s; PageTitle:%s; Failed to synchronize data to show-doc,err:", r.showDocDetails.CatName, r.showDocDetails.PageTitle)
	body, err := json.Marshal(r.showDocDetails)
	if err != nil {
		logs.Error(errInfo, err.Error())
		return
	}
	post, err := http.Post(r.showDocDetails.Url, "application/json", io.NopCloser(bytes.NewBuffer(body)))
	if err != nil {
		logs.Error(errInfo, err.Error())
		return
	}
	all, err := io.ReadAll(post.Body)
	if err != nil {
		logs.Error(errInfo, err.Error())
		return
	}
	resp := new(showDocResp)
	if err = json.Unmarshal(all, &resp); err != nil {
		logs.Error(errInfo, fmt.Sprintf("show-doc response format failed,%s", err.Error()))
		return
	}
	if resp.ErrorCode != 0 {
		logs.Error(errInfo, fmt.Sprintf("error code[%f]:%s", resp.ErrorCode, resp.ErrorMessage))
		return
	}
	logs.Info("Success to synchronize data to show-doc---CatName:%s; PageTitle:%s", r.showDocDetails.CatName, r.showDocDetails.PageTitle)
}

func (r *resolver) getSwagger() (swagger string) {
	r.jq.Reset().From("swagger").Out(&swagger)
	if err := r.jq.Error(); err != nil {
		logs.Warn(err.Error())
	}
	return swagger
}

func (r *resolver) getinfo() (info map[string]interface{}) {
	info = make(map[string]interface{})
	r.jq.Reset().Reset().From("info").Out(&info)
	if err := r.jq.Error(); err != nil {
		logs.Warn(err.Error())
	}
	return info
}

func (r *resolver) getHost() (host string) {
	r.jq.Reset().Reset().From("host").Out(&host)
	if err := r.jq.Error(); err != nil {
		logs.Warn(err.Error())
	}
	return host
}

func (r *resolver) getBasePath() (basePath string) {
	r.jq.Reset().Reset().From("basePath").Out(&basePath)
	if err := r.jq.Error(); err != nil {
		logs.Warn(err.Error())
	}
	return basePath
}

func (r *resolver) gettags() (tags []map[string]interface{}) {
	tags = make([]map[string]interface{}, 0)
	r.jq.Reset().Reset().From("tags").Out(&tags)
	if err := r.jq.Error(); err != nil {
		logs.Warn(err.Error())
	}
	return tags
}

func (r *resolver) getSchemes() (schemes []string) {
	schemes = make([]string, 0)
	r.jq.Reset().Reset().From("schemes").Out(&schemes)
	if err := r.jq.Error(); err != nil {
		logs.Warn(err.Error())
	}
	return schemes
}

func (r *resolver) getComponents() {
	if r.components != nil {
		return
	}
	r.components = new(Components)
	r.jq.Reset().Reset().From("components").Out(&r.components)
	if err := r.jq.Error(); err != nil {
		logs.Warn(err.Error())
	}
}

func (r *resolver) getPaths() (paths map[string]interface{}) {
	paths = make(map[string]interface{})
	r.jq.Reset().Reset().From("paths").Out(&paths)
	if err := r.jq.Error(); err != nil {
		logs.Warn(err.Error())
	}
	return paths
}

func (r *resolver) getSecurityDefinitions() (securityDefinitions map[string]interface{}) {
	securityDefinitions = make(map[string]interface{})
	r.jq.Reset().Reset().From("securityDefinitions").Out(&securityDefinitions)
	if err := r.jq.Error(); err != nil {
		logs.Warn(err.Error())
	}
	return securityDefinitions
}

func (r *resolver) getDefinitions() {
	if r.definitions != nil || len(r.definitions) != 0 {
		return
	}
	r.definitions = make(map[string]interface{})
	r.jq.Reset().Reset().From("definitions").Out(&r.definitions)
	if err := r.jq.Error(); err != nil {
		logs.Warn(err.Error())
	}
}

func (r *resolver) getExternalDocs() (externalDocs map[string]interface{}) {
	externalDocs = make(map[string]interface{})
	r.jq.Reset().Reset().From("externalDocs").Out(&externalDocs)
	if err := r.jq.Error(); err != nil {
		logs.Warn(err.Error())
	}
	return externalDocs
}

func (r *resolver) parseDetails() *DetailsInfo {
	d := new(DetailsInfo)
	deserialization(r.getinfo(), d)
	return d
}

func (r *resolver) parseTags() []TagsInfo {
	t := make([]TagsInfo, 0)
	deserialization(r.gettags(), &t)
	return t
}

func deserialization(from, to interface{}) {
	marshal, err := json.Marshal(from)
	if err != nil {
		panic(err)
	}
	if err = json.Unmarshal(marshal, to); err != nil {
		panic(err)
	}
}

func serialization(data interface{}) string {
	indent, err := json.MarshalIndent(data, "", "     ")
	if err != nil {
		panic(err)
	}
	return string(indent)
}

func debugObject(data interface{}) {
	indent, _ := json.MarshalIndent(data, "", "     ")
	println(string(indent))
}
